package mn.more.foundation.bean;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.StringTokenizer;
import mn.more.foundation.lang.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * @author $Author: mikeliucc $
 * @version $Id: BeanUtil.java 5 2008-09-01 12:08:42Z mikeliucc $
 */
public final class BeanUtil {

	private static final Logger LOG = Logger.getLogger(BeanUtil.class);
	public static final String NESTING_TOKEN = ".";
	public static final String SET_PREFIX = "set";
	public static final String GET_PREFIX = "get";
	public static final String ARRAY_TYPE_SUFFIX = "[]";

	private BeanUtil() { }

	public static Object getProperty(Object bean, String propertyName)
		throws IllegalAccessException,
		       InvocationTargetException,
		       NoSuchMethodException {
		if (bean == null || StringUtils.isBlank(propertyName)) { return null; }

		StringTokenizer nestedNames =
			new StringTokenizer(propertyName, NESTING_TOKEN);
		Object currentObject = bean;
		while (nestedNames.hasMoreTokens()) {
			String name = nestedNames.nextToken();
			Method method = currentObject.getClass().getMethod(
				GET_PREFIX + StringUtil.toTitleCase(name));
			if (method == null) { break; }

			if (!nestedNames.hasMoreTokens()) {
				return method.invoke(currentObject);
			} else {
				currentObject = method.invoke(currentObject);
			}
		}

		return null;
	}

	public static void setProperty(Object bean,
	                               String propertyName,
	                               Object value)
		throws IllegalAccessException, InvocationTargetException {
		if (bean == null || StringUtil.isBlank(propertyName) || value == null) {
			return;
		}

		StringTokenizer nestedNames =
			new StringTokenizer(propertyName, NESTING_TOKEN);
		Object currentObject = bean;
		while (nestedNames.hasMoreTokens()) {
			String name = nestedNames.nextToken();
			Class<? extends Object> currentBeanClass = currentObject.getClass();
			try {
				String methodName;
				if (!nestedNames.hasMoreTokens()) {
					methodName = SET_PREFIX + StringUtil.toTitleCase(name);
					Method method = currentBeanClass.getMethod(methodName,
					                                           value.getClass());
					if (method == null) { break; }

					method.invoke(currentObject, value);
				} else {
					methodName = GET_PREFIX + StringUtil.toTitleCase(name);
					Method method = currentBeanClass.getMethod(methodName);
					if (method == null) { break; }

					currentObject = method.invoke(currentObject);
				}
			} catch (NoSuchMethodException e) {
				LOG.warn("no setter for '" + name +
				         "(" + value.getClass() + ")' found for bean " +
				         currentBeanClass.getName() +
				         ": " + e.getMessage());
				break;
			}
		}
	}

	/**
	 * @deprecated should just use java's {@link Constructor#newInstance(Object[])}
	 *             method. Java 6 does a pretty job there. :-)
	 */
	public static Object newInstance(Class beanClass)
		throws
		NoSuchMethodException,
		IllegalAccessException,
		InvocationTargetException,
		InstantiationException {
		Constructor constructor = beanClass.getConstructor();
		return constructor.newInstance();
	}

	public static Class getClass(String className)
		throws ClassNotFoundException {
		if (StringUtil.isBlank(className)) { return null; }

		if (className.endsWith(ARRAY_TYPE_SUFFIX)) {
			Class clazz =
				Class.forName(className.substring(0,
				                                  className.length() -
				                                  ARRAY_TYPE_SUFFIX.length()),
				              true,
				              Thread.currentThread().getContextClassLoader());
			if (clazz == null) { return null; }
			if (clazz.isArray()) {
				return clazz;
			} else {
				Object temp = Array.newInstance(clazz, new int[]{1});
				if (temp == null) { return null; }
				return temp.getClass();
			}
		} else {
			return Class.forName(
				className,
				true,
				Thread.currentThread().getContextClassLoader());
		}
	}
}
